package com.tian.service.impl;

import com.tian.convert.UserCouponRecordConvertUtil;
import com.tian.dto.*;
import com.tian.entity.ActivityCoupon;
import com.tian.entity.UserCouponRecord;
import com.tian.entity.UserPoint;
import com.tian.entity.UserPointRecord;
import com.tian.enums.ResultCode;
import com.tian.enums.StatusEnums;
import com.tian.enums.UserPointRecordTypeEnum;
import com.tian.mapper.ActivityCouponMapper;
import com.tian.mapper.UserCouponRecordMapper;
import com.tian.mapper.UserPointMapper;
import com.tian.mapper.UserPointRecordMapper;
import com.tian.service.ActivityCouponService;
import com.tian.service.UserCouponService;
import com.tian.util.CommonResult;
import com.tian.util.RedisConstantPre;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;

/**
 * @author tianwc  公众号：java后端技术全栈、面试专栏
 * @version 1.0.0
 * @date 2023年05月15日 15:09
 * 博客地址：<a href="http://woaijava.cc/">博客地址</a>
 * <p>
 * 用户领取优惠券
 */
@Service("userCouponService")
public class UserCouponServiceImpl implements UserCouponService {
    @Resource
    private UserCouponRecordMapper userCouponRecordMapper;
    @Resource
    private UserPointMapper userPointMapper;
    @Resource
    private ActivityCouponService activityCouponService;
    @Resource
    private UserPointRecordMapper userPointRecordMapper;
    @Resource
    private ActivityCouponMapper activityCouponMapper;

    @Resource
    private RedissonClient redissonClient;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public CommonResult<Boolean> receive(UserCouponAddDto userCouponAddDto) {

        boolean need = checkCouponNeedLock(userCouponAddDto.getActivityId(), userCouponAddDto.getCouponId());
        //key=前缀+活动id+优惠券id
        String key = RedisConstantPre.USER_COUPON_PRE + userCouponAddDto.getActivityId() + userCouponAddDto.getCouponId();
        if (need) {
            redissonClient.getLock(key).lock();
        }
        try {
            CommonResult<ActivityCoupon> commonResult = activityCouponService.check(userCouponAddDto);
            if (commonResult.getCode() != ResultCode.SUCCESS.getCode()) {
                return CommonResult.failed(commonResult.getMessage());
            }
            ActivityCoupon activityCoupon = commonResult.getData();

            int count = userCouponRecordMapper.countByUserIdAndCouponId(userCouponAddDto.getUserId(), userCouponAddDto.getCouponId());

            if (count >= activityCoupon.getLimitPerUser()) {
                return CommonResult.failed(ResultCode.USER_COUPON_LIMITER);
            }
            if (activityCoupon.getTotalCount() <= activityCoupon.getRemainCount()) {
                return CommonResult.failed(ResultCode.TREMAIN_COUNT_LIMITER);
            }
            redissonClient.getLock(RedisConstantPre.USER_POINT_LOCK_PRE + userCouponAddDto.getUserId()).lock();

            UserPoint userPoint = userPointMapper.selectByUserId(userCouponAddDto.getUserId());
            if (activityCoupon.getPoint() > userPoint.getPoints()) {
                return CommonResult.failed(ResultCode.ACTIVITY_NO_COUPON);
            }
            int point = userPoint.getPoints() - activityCoupon.getPoint();
            userPoint.setPoints(point);

            userPointMapper.updateByPrimaryKey(userPoint);

            UserCouponRecord userCouponRecord = UserCouponRecordConvertUtil.convert(userCouponAddDto);

            userCouponRecordMapper.insert(userCouponRecord);
            String oderNo = "USER_COUPON_RECEIVE_" + userPoint.getUserId();

            addUserPointRecord(activityCoupon.getPoint(), UserPointRecordTypeEnum.DELETE.getType(), userPoint.getUserId(),
                    StatusEnums.SUCCESS.getStatus(), new Date(), oderNo);
        } finally {
            redissonClient.getLock(RedisConstantPre.USER_POINT_LOCK_PRE + userCouponAddDto.getUserId()).unlock();
            if (need) {
                redissonClient.getLock(key).unlock();
            }
        }
        return CommonResult.success(Boolean.TRUE);
    }

    private boolean checkCouponNeedLock(Integer activityId, Integer couponId) {
        ActivityCoupon activityCoupon = activityCouponMapper.selectByActivityCoupon(activityId, couponId);
        if (activityCoupon == null || activityCoupon.getTotalCount().intValue() == 0) {
            return false;
        }
        return true;
    }

    @Override
    public CommonResult<UserCouponRecordListRespDto> list(UserCouponRecordReqDto userCouponRecordReqDto) {
        UserCouponRecordDto userCouponRecordDto = new UserCouponRecordDto();
        int count = userCouponRecordMapper.count(userCouponRecordDto);
        if (count == 0) {
            return CommonResult.success(new UserCouponRecordListRespDto(userCouponRecordReqDto.getStart(), userCouponRecordReqDto.getPageSize(), 0));
        }
        List<UserCouponRecord> userCouponRecordList = userCouponRecordMapper.list(userCouponRecordDto);

        UserCouponRecordListRespDto userCouponRecordListRespDto = new UserCouponRecordListRespDto(userCouponRecordReqDto.getStart(), userCouponRecordReqDto.getPageSize(), count);
        userCouponRecordListRespDto.setList(UserCouponRecordConvertUtil.convert(userCouponRecordList));

        return CommonResult.success(userCouponRecordListRespDto);
    }

    private void addUserPointRecord(int point, int type, Long userId, int status, Date createTime, String orderNo) {
        UserPointRecord record = new UserPointRecordDto();
        record.setPoint(point);
        record.setUserId(userId);
        record.setStatus(status);
        record.setCreateTime(createTime);
        record.setType(type);
        record.setOrderNo(orderNo);
        record.setUpdateTime(createTime);
        userPointRecordMapper.insert(record);
    }
}
